/**
 * Copyright 2019 吉鼎科技.
 *
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.lang.Files;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.SimpleTextRequestMessage;
import cn.easyplatform.messages.request.vfs.DirRequestMessage;
import cn.easyplatform.messages.request.vfs.WriteRequestMessage;
import cn.easyplatform.messages.vos.VfsVo;
import cn.easyplatform.spi.service.VfsService;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.web.dialog.MessageBox;
import cn.easyplatform.web.ext.Assignable;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.zul.Explorer;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.utils.FileUtil;
import cn.easyplatform.web.utils.WebUtils;
import io.keikai.api.Exporter;
import io.keikai.api.Exporters;
import io.keikai.api.Importer;
import io.keikai.api.Importers;
import io.keikai.api.model.Book;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.LoggerFactory;
import org.zkoss.lang.Objects;
import org.zkoss.util.media.AMedia;
import org.zkoss.util.media.Media;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.WrongValueException;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.event.UploadEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zkex.zul.Pdfviewer;
import org.zkoss.zkmax.zul.Filedownload;
import org.zkoss.zkmax.zul.Video;
import org.zkoss.zul.*;

import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;


/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class ExplorerBuilder implements ComponentBuilder, Assignable {

    private Explorer exp;

    private String baseDir;

    private HtmlBasedComponent container;

    private List<String> paths;

    private OperableHandler main;

    private boolean isLocal;

    public ExplorerBuilder(OperableHandler mainTaskHandler, Explorer exp) {
        this.main = mainTaskHandler;
        this.exp = exp;
    }

    @Override
    public Component build() {
        exp.setAttribute("$proxy", this);
        //exp.setSclass("z-grid");
        if (Strings.isBlank(exp.getStyle()))
            exp.setStyle("padding:0px");
        else
            exp.setStyle("padding:0px;" + exp.getStyle());
        if (exp.isMkdir())
            exp.setFolder(true);
        //if (exp.isFolder())
        paths = new ArrayList<>();
        if (exp.isToolbar()) {
            Toolbar toolbar = new Toolbar();
            Toolbarbutton folder = new Toolbarbutton();
            folder.setIconSclass("z-icon-folder");
            folder.setParent(toolbar);
            folder.setLabel(Labels.getLabel("explorer.folder.create"));
            folder.setVisible(exp.isMkdir());
            if (exp.isMkdir()) {
                folder.addEventListener(Events.ON_CLICK,
                        new EventListener<Event>() {
                            @Override
                            public void onEvent(Event event) throws Exception {
                                final Window win = new Window(Labels
                                        .getLabel("explorer.folder.create"), "normal",
                                        true);
                                win.setHeight("150px");
                                win.setWidth("350px");
                                Vlayout vl = new Vlayout();
                                vl.setSpacing("30px");
                                vl.setHflex("1");
                                vl.setVflex("1");

                                Hlayout hl = new Hlayout();
                                hl.setHflex("1");
                                hl.appendChild(new Label(Labels
                                        .getLabel("explorer.folder.name") + ":"));
                                final Textbox tb = new Textbox();
                                tb.setHflex("1");
                                hl.appendChild(tb);
                                hl.setParent(vl);

                                Div div = new Div();
                                div.setZclass("text-center");
                                Button ok = new Button(Labels
                                        .getLabel("button.ok"));
                                ok.setIconSclass("z-icon-check");
                                ok.addEventListener(Events.ON_CLICK,
                                        new EventListener<Event>() {
                                            @Override
                                            public void onEvent(Event event)
                                                    throws Exception {
                                                String name = tb.getValue();
                                                if (!Strings.isBlank(name)) {
                                                    String dir = paths.get(paths.size() - 1) + "/" + name;
                                                    VfsVo file = new VfsVo();
                                                    file.setPath(baseDir + dir);
                                                    file.setName(name);
                                                    file.setFolder(true);
                                                    if (isLocal) {
                                                        FileUtils.forceMkdir(new File(WebUtils.getFile(file.getPath())));
                                                        createNew(file);
                                                        win.detach();
                                                    } else {
                                                        IResponseMessage<?> resp = ServiceLocator.lookup(VfsService.class).mkdir(new SimpleTextRequestMessage(main.getId(), file.getPath()));
                                                        if (!resp.isSuccess())
                                                            Clients.wrongValue(event.getTarget(), (String) resp.getBody());
                                                        else {
                                                            createNew(file);
                                                            win.detach();
                                                        }
                                                    }
                                                }
                                            }
                                        });
                                ok.setParent(div);
                                div.setParent(vl);

                                vl.setParent(win);
                                win.setPage(exp.getPage());
                                win.doHighlighted();
                            }
                        });
            }

            Toolbarbutton upload = new Toolbarbutton();
            upload.setIconSclass("z-icon-upload");
            upload.setParent(toolbar);
            upload.setLabel(Labels.getLabel("explorer.upload"));
            upload.setVisible(exp.isUpload());
            if (exp.isUpload()) {
                StringBuilder sb = new StringBuilder();
                sb.append("true,native").append(",maxsize=")
                        .append(exp.getMaxSize()).append(",multiple=")
                        .append(exp.isMultiple());
                if (!Strings.isBlank(exp.getAccept()))
                    sb.append(",accept=").append(exp.getAccept());
                upload.setUpload(sb.toString());
                upload.addEventListener(Events.ON_UPLOAD,
                        new EventListener<UploadEvent>() {
                            @Override
                            public void onEvent(UploadEvent event)
                                    throws Exception {
                                if (event.getMedias() == null) {//上传文件过大
                                    MessageBox.showMessage(Labels.getLabel("message.dialog.title.error"), Labels.getLabel("explorer.upload.max"));
                                } else {
                                    String[] accepts = {};
                                    if (!Strings.isBlank(exp.getAccept()) && !"none".equals(exp.getAccept())) {
                                        accepts = exp.getAccept().toLowerCase().split("\\|");
                                        for (int i = 0; i < accepts.length; i++) {
                                            if (!accepts[i].startsWith(".") && accepts[i].indexOf("/") > 0)
                                                accepts[i] = StringUtils.substringBefore(accepts[i], "/");
                                        }
                                    }
                                    for (Media media : event.getMedias()) {
                                        if (accepts.length > 0) {
                                            boolean isAccepted = false;
                                            for (String accept : accepts) {
                                                if (!isAccepted) {
                                                    if (accept.startsWith(".")) {
                                                        isAccepted = media.getName().toLowerCase().endsWith(accept);
                                                    } else {
                                                        isAccepted = media.getContentType().toLowerCase().startsWith(accept);
                                                    }
                                                }
                                            }
                                            if (!isAccepted) {
                                                Clients.wrongValue(event.getTarget(), Labels.getLabel("explorer.file.reject"));
                                                return;
                                            }
                                        }
                                        String dir = paths.get(paths.size() - 1)
                                                + "/" + media.getName();
                                        VfsVo fv = new VfsVo();
                                        fv.setPath(baseDir + dir);
                                        fv.setName(media.getName());
                                        fv.setFolder(false);
                                        byte[] data = null;
                                        try {
                                            data = media.getByteData();
                                        } catch (IllegalStateException e) {
                                            data = IOUtils.toByteArray(media.getStreamData());
                                        }
                                        if (isLocal) {
                                            FileUtils.writeByteArrayToFile(new File(WebUtils.getFile(fv.getPath())),
                                                    data);
                                            createNew(fv);
                                        } else {
                                            fv.setData(data);
                                            IResponseMessage<?> resp = ServiceLocator.lookup(VfsService.class).write(new WriteRequestMessage(main.getId(), fv));
                                            if (!resp.isSuccess())
                                                Clients.wrongValue(event.getTarget(), (String) resp.getBody());
                                            else
                                                createNew(fv);
                                        }
                                    }
                                }
                            }
                        });
            }

            Toolbarbutton prev = new Toolbarbutton();
            prev.setIconSclass("z-icon-arrow-left");
            prev.setParent(toolbar);
            prev.setLabel(Labels.getLabel("button.back"));
            prev.setVisible(exp.isFolder());
            if (exp.isFolder()) {
                prev.addEventListener(Events.ON_CLICK,
                        new EventListener<Event>() {
                            @Override
                            public void onEvent(Event event) throws Exception {
                                if (paths.size() > 1) {
                                    paths.remove(paths.size() - 1);
                                    String dir = paths.get(paths.size() - 1);
                                    exp.getLastChild().detach();
                                    load(dir);
                                }
                            }
                        });
            }

            Toolbarbutton refresh = new Toolbarbutton();
            refresh.setIconSclass("z-icon-refresh");
            refresh.setParent(toolbar);
            refresh.setLabel(Labels.getLabel("explorer.refresh"));
            refresh.addEventListener(Events.ON_CLICK,
                    new EventListener<Event>() {
                        @Override
                        public void onEvent(Event event) throws Exception {
                            exp.getLastChild().detach();
                            load(paths.get(paths.size() - 1));
                        }
                    });

            toolbar.setParent(exp);
        }
        if (!Strings.isBlank(exp.getSrc()))
            init();
        return exp;
    }

    private void init() {
        baseDir = exp.getSrc();
        isLocal = !baseDir.startsWith("$707") && !baseDir.startsWith("$729");
        String dir = "";
        if (isLocal) {
            File file = new File(WebUtils.getFile(baseDir));
            if (file.exists()) {
                if (paths != null) {
                    paths.clear();
                    paths.add(dir);
                }
                load(dir);
            } else if (exp.isForce()) {
                if (Files.makeDir(file)) {
                    if (paths != null) {
                        paths.clear();
                        paths.add(dir);
                    }
                    load(dir);
                } else
                    throw new WrongValueException(exp, Labels.getLabel(
                            "explorer.dir.exist", new String[]{baseDir}));
            } else
                throw new WrongValueException(exp, Labels.getLabel(
                        "explorer.dir.exist", new String[]{baseDir}));
        } else {
            if (paths != null) {
                paths.clear();
                paths.add(dir);
            }
            load(dir);
        }
    }

    private void createNew(VfsVo file) {
        if (container.getChildren().size() % exp.getCols() == 0) {
            Div row = new Div();
            row.setZclass("row");
            container.appendChild(row);
            createItem(row, file);
        } else {
            createItem(container.getLastChild(), file);
        }
    }

    private void load(String dir) {
        if (isLocal) {
            File folder = new File(WebUtils.getFile(baseDir + dir));
            File[] files = folder.listFiles(new FileFilter() {
                @Override
                public boolean accept(File f) {
                    if (!exp.isFolder())
                        return !f.isHidden() && f.isFile()
                                && !f.getName().startsWith(".");
                    return !f.isHidden() && !f.getName().startsWith(".");
                }
            });
            if (files != null) {
                VfsVo[] vfs = new VfsVo[files.length];
                for (int i = 0; i < files.length; i++) {
                    vfs[i] = new VfsVo();
                    vfs[i].setFolder(files[i].isDirectory());
                    vfs[i].setPath(baseDir + dir + "/" + files[i].getName());
                    vfs[i].setName(files[i].getName());
                    vfs[i].setModified(files[i].lastModified());
                    if (files[i].isFile())
                        vfs[i].setSize(files[i].length());
                }
                list(vfs);
            }
        } else {
            VfsService vfs = ServiceLocator.lookup(VfsService.class);
            IResponseMessage<?> resp = vfs.dir(new DirRequestMessage(main.getId(), baseDir + dir, exp.isForce()));
            if (!resp.isSuccess())
                Clients.wrongValue(exp, (String) resp.getBody());
            else {
                VfsVo[] files = (VfsVo[]) resp.getBody();
                list(files);
            }
        }
    }

    private void list(VfsVo[] files) {
        container = new Div();
        container.setZclass("w-100 h-100 border rounded p-2");
        container.setStyle("min-height:28px");
        Div row = null;
        int i = 0;
        if (exp.getCols() <= 0)
            exp.setCols(4);
        for (VfsVo f : files) {
            if (i % exp.getCols() == 0) {
                row = new Div();
                row.setZclass("row");
                container.appendChild(row);
            }
            createItem(row, f);
            i++;
        }
        container.setParent(exp);
    }

    private void createItem(Component parent, final VfsVo file) {
        Div div = new Div();
        div.setZclass("col-12 col-md-6 col-lg-3 mb-1");
        Combobutton cb = new Combobutton();
        cb.setLabel(file.getName());
        if (file.isFolder()) {
            cb.setIconSclass("z-icon-folder");
        } else {
            String ext = FilenameUtils.getExtension(file.getName());
            if (ext.equalsIgnoreCase("doc") || ext.equalsIgnoreCase("docx"))
                cb.setIconSclass("z-icon-file-word-o");
            else if (ext.equalsIgnoreCase("xls")
                    || ext.equalsIgnoreCase("xlsx")) {
                cb.setIconSclass("z-icon-file-excel-o");
            } else if (ext.equalsIgnoreCase("ppt")
                    || ext.equalsIgnoreCase("pptx")) {
                cb.setIconSclass("z-icon-file-powerpoint-o");
            } else if (ext.equalsIgnoreCase("pdf")) {
                cb.setIconSclass("z-icon-file-pdf-o");
            } else if (ext.equalsIgnoreCase("zip")
                    || ext.equalsIgnoreCase("rar")
                    || ext.equalsIgnoreCase("jar")
                    || ext.equalsIgnoreCase("arz")
                    || ext.equalsIgnoreCase("gz") || ext.equalsIgnoreCase("z")) {
                cb.setIconSclass("z-icon-file-zip-o");
            } else if (ext.equalsIgnoreCase("txt")) {
                cb.setIconSclass("z-icon-file-text-o");
            } else if (ext.equalsIgnoreCase("bmp")
                    || ext.equalsIgnoreCase("png")
                    || ext.equalsIgnoreCase("gif")
                    || ext.equalsIgnoreCase("jpg")
                    || ext.equalsIgnoreCase("pic")
                    || ext.equalsIgnoreCase("tif")) {
                cb.setIconSclass("z-icon-image");
            } else if (ext.equalsIgnoreCase("mp3")
                    || ext.equalsIgnoreCase("mpga")
                    || ext.equalsIgnoreCase("ogg")
                    || ext.equalsIgnoreCase("wav")
                    || ext.equalsIgnoreCase("wma")
                    || ext.equalsIgnoreCase("wmv")
                    || ext.equalsIgnoreCase("m3u")
                    || ext.equalsIgnoreCase("m4a")
                    || ext.equalsIgnoreCase("m4b")
                    || ext.equalsIgnoreCase("m4p")) {
                cb.setIconSclass("z-icon-file-audio-o");
            } else if (ext.equalsIgnoreCase("3gp")
                    || ext.equalsIgnoreCase("asf")
                    || ext.equalsIgnoreCase("avi")
                    || ext.equalsIgnoreCase("m4u")
                    || ext.equalsIgnoreCase("m4v")
                    || ext.equalsIgnoreCase("mov")
                    || ext.equalsIgnoreCase("mp4")
                    || ext.equalsIgnoreCase("mpg4")
                    || ext.equalsIgnoreCase("mpe")
                    || ext.equalsIgnoreCase("mpeg")
                    || ext.equalsIgnoreCase("mpg")) {
                cb.setIconSclass("z-icon-file-video-o");
            } else {
                cb.setIconSclass("z-icon-file-o");
            }
        }
        if (exp.isDelete() || exp.isPreview() || exp.isDownload()) {
            Menupopup mp = new Menupopup();
            if (file.isFolder()) {
                Menuitem mi = new Menuitem(Labels.getLabel("button.open"));
                mi.setIconSclass("z-icon-folder-open");
                mi.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
                    @Override
                    public void onEvent(Event event) throws Exception {
                        String dir = File.separator + file.getName();
                        paths.add(dir);
                        exp.getLastChild().detach();
                        load(dir);
                    }
                });
                mi.setParent(mp);
            }
            if (exp.isDelete()) {
                Menuitem mi = new Menuitem(Labels.getLabel("button.delete"));
                mi.setIconSclass("z-icon-times");
                mi.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
                    @Override
                    public void onEvent(Event event) throws Exception {
                        if (Events.ON_CLICK.equals(event.getName())) {
                            Messagebox.show(
                                    Labels.getLabel("explorer.delete.confirm"),
                                    Labels.getLabel("message.dialg.title.warning"),
                                    Messagebox.CANCEL | Messagebox.OK,
                                    Messagebox.QUESTION, this);
                        } else if (Events.ON_OK.equals(event.getName())) {
                            if (isLocal) {
                                FileUtils.forceDelete(new File(WebUtils.getFile(file.getPath())));
                                cb.detach();
                            } else {
                                IResponseMessage<?> resp = ServiceLocator.lookup(VfsService.class).delete(new SimpleTextRequestMessage(main.getId(), file.getPath()));
                                if (!resp.isSuccess())
                                    Clients.wrongValue(event.getTarget(), (String) resp.getBody());
                                else
                                    cb.detach();
                            }
                            String dir = paths.get(paths.size() - 1);
                            exp.getLastChild().detach();
                            load(dir);
                        }
                    }
                });
                mi.setParent(mp);
            }
            if (!file.isFolder() && exp.isDownload()) {
                Menuitem mi = new Menuitem(Labels.getLabel("button.download"));
                mi.setIconSclass("z-icon-download");
                mi.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
                    @Override
                    public void onEvent(Event event) throws Exception {
                        Filedownload.save(WebUtils.getFileContent(file.getPath()), null, file.getName());
                    }
                });
                mi.setParent(mp);
            }
            if (!file.isFolder() && exp.isPreview() && canPreview(FilenameUtils.getExtension(file.getName()))) {
                Menuitem mi = new Menuitem(Labels.getLabel("explorer.preview"));
                mi.setIconSclass("z-icon-eye");
                mi.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
                    @Override
                    public void onEvent(Event event) throws Exception {
                        FileUtil.preview(event.getPage(), file.getPath());
                    }
                });
                mi.setParent(mp);
            }
            cb.appendChild(mp);
        } else if (file.isFolder()) {
            cb.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
                @Override
                public void onEvent(Event event) throws Exception {
                    String dir = File.separator + file.getName();
                    paths.add(dir);
                    exp.getLastChild().detach();
                    load(dir);
                }
            });
        }
        div.appendChild(cb);
        parent.appendChild(div);
    }

    private boolean canPreview(String ext) {
        return //ext.startsWith("doc") || ext.startsWith("ppt")||
                ext.startsWith("xls") || ext.startsWith("pdf")
                        || ext.equalsIgnoreCase("bmp")
                        || ext.equalsIgnoreCase("png")
                        || ext.equalsIgnoreCase("gif")
                        || ext.equalsIgnoreCase("jpg")
                        || ext.equalsIgnoreCase("pic")
                        || ext.equalsIgnoreCase("tif")
                        || ext.equalsIgnoreCase("mp3")
                        || ext.equalsIgnoreCase("mpga")
                        || ext.equalsIgnoreCase("ogg")
                        || ext.equalsIgnoreCase("wav")
                        || ext.equalsIgnoreCase("wma")
                        || ext.equalsIgnoreCase("wmv")
                        || ext.equalsIgnoreCase("m3u")
                        || ext.equalsIgnoreCase("m4a")
                        || ext.equalsIgnoreCase("m4b")
                        || ext.equalsIgnoreCase("m4p")
                        || ext.equalsIgnoreCase("3gp")
                        || ext.equalsIgnoreCase("asf")
                        || ext.equalsIgnoreCase("avi")
                        || ext.equalsIgnoreCase("m4u")
                        || ext.equalsIgnoreCase("m4v")
                        || ext.equalsIgnoreCase("mov")
                        || ext.equalsIgnoreCase("mp4")
                        || ext.equalsIgnoreCase("mpg4")
                        || ext.equalsIgnoreCase("mpe")
                        || ext.equalsIgnoreCase("mpeg")
                        || ext.equalsIgnoreCase("mpg");

    }

    @Override
    public void setValue(Object value) {
        if (!Objects.equals(exp.getSrc(), value)) {
            this.exp.setSrc((String) value);
            if (exp.isToolbar()) {
                if (exp.getChildren().size() == 2)
                    exp.getLastChild().detach();
            } else
                exp.getLastChild().detach();
            init();
        }
    }

    @Override
    public Object getValue() {
        return exp.getSrc();
    }
}
